package lesson4.example2;

import java.io.File;
import java.io.IOException;

import org.apache.commons.io.FileUtils;

import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.commons.AdviceAdapter;

import com.classloading.utils.JavaCompilerUtil;

class TestAOP extends AdviceAdapter implements Opcodes{
	String name;
	public TestAOP( final int api,
		      final MethodVisitor mv,
		      final int access,
		      final String name,
		      final String desc){
		super(ASM7, mv, access, name, desc);
	    this.name = name;
	}
	
	protected void onMethodEnter() {
		super.onMethodEnter();
		mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
		mv.visitLdcInsn("enter method：" + name);
		mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
	}

	protected void onMethodExit(int opcode) {
		super.onMethodExit(opcode);
		mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
		if (opcode == ATHROW) {
			mv.visitLdcInsn("error exit method：" + name);
		} else {
			mv.visitLdcInsn("normal exit method：" + name);
		}
		mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
	}
}

public class TestASM {

	public static String path = JavaCompilerUtil.path +"/bytecode-examples/bytecode-example-4-01/src/main/java/";
	public static String destpath = JavaCompilerUtil.path +"/bytecode-examples/bytecode-example-4-01/output";

	public static void main(String args[]) throws IOException {
		JavaCompilerUtil.compile(path + "/lesson4/example2/TestDemo.java", destpath);
		testAOPByASM();
//		JavaCompilerUtil.decompile(destpath + "/lesson4/example2/TestDemo.class");
//		 JavaCompilerUtil.run(new String[]{
//		   "java","-cp",destpath,"lesson4.example2.TestDemo"
//		 });
	}

	public static void testAOPByASM() throws IOException{
		byte[] bytes = FileUtils.readFileToByteArray(new File(destpath + "/lesson4/example2/TestDemo.class"));
		ClassReader classReader = new ClassReader(bytes);
		ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
		
		ClassVisitor classVisitor = new ClassVisitor(Opcodes.ASM7, classWriter) {
			public MethodVisitor visitMethod(
				int      access, 
				String   name, 
				String   desc, 
				String   signature,
				String[] exceptions
		    ){
				MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
				if (!"foo".equals(name)) {
					return mv;
				}
				// AdviceAdapter方便开发者在方法开始后和结束前插入代码
				return new TestAOP(Opcodes.ASM7,mv,access,name,desc);
			}
		};

		classReader.accept(classVisitor, 0);
		byte[] bytesModified = classWriter.toByteArray();
		FileUtils.writeByteArrayToFile(new File(destpath + "/lesson4/example2/TestDemo.class"), bytesModified);
	}
}
